/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Filters;

import java.util.Arrays;

/**
 *
 * @author Gebruiker
 */
public class MWA {

    /**
     * Calculates a new, filtered data series from the origignal, using the
     * moving window average technique.
     * @param source The source data.
     * @param windowSize How many numbers on each side of the inspected number
     * should be used for the average, not counting the inspected number.
     * @return The filtered data.
     */
    public static double[] normal(double[] source, int windowSizeLeft, int windowSizeRight) {
        double[] result = new double[source.length];

        for (int i = 0; i < source.length; i++) {
            int sizeLeft = i;
            int sizeRight = source.length - i - 1;

            if (sizeLeft > windowSizeLeft) {
                sizeLeft = windowSizeLeft;
            }
            if (sizeRight > windowSizeRight) {
                sizeRight = windowSizeRight;
            }

            double sum = 0;
            //Get left side
            for (int x = i - sizeLeft; x < i; x++) {
                sum += source[x];
            }
            //Get number itself
            sum += source[i];
            //Get right side
            for (int y = i + sizeRight; y > i; y--) {
                sum += source[y];
            }
            result[i] = sum / (sizeLeft + 1 + sizeRight);
        }
        return result;
    }

    /**
     * Returns the length the resulting data set would have if the WMA filter is applied.
     * @param sourceLength
     * @param leftWindowSize
     * @param rightWindowSize
     * @return 
     */
    public static int resultSize(int sourceLength, int leftWindowSize, int rightWindowSize) {
        return (sourceLength - leftWindowSize - rightWindowSize);
    }

    /**
     * Calculates a new, filtered data series from the origignal, using the
     * moving window average technique and the median of the window.
     * @param source
     * @param windowSizeLeft
     * @param windowSizeRight
     * @return 
     */
    public static double[] median(double[] source, int windowSizeLeft, int windowSizeRight) {
        double[] result = new double[source.length - windowSizeLeft - windowSizeRight];

        int counter = 0;
        for (int i = 0; i < source.length; i++) {
            if (i > windowSizeLeft - 1 && i < source.length - windowSizeRight) {
                double[] range = new double[windowSizeLeft + windowSizeRight + 1];
                int rangeCounter = 0;
                //Get left side
                for (int x = i - windowSizeLeft; x < i; x++) {
                    range[rangeCounter] = source[x];
                    rangeCounter++;
                }
                //Get number itself
                range[rangeCounter] += source[i];
                rangeCounter++;
                //Get right side
                for (int y = i + windowSizeRight; y > i; y--) {
                    range[rangeCounter] += source[y];
                    rangeCounter++;
                }
                int resultIndex = (int) Math.floor(range.length / 2);
                Arrays.sort(range);
                result[counter] = range[resultIndex];
                counter++;
            }
        }
        return result;
    }

    /**
     * 
     * @param source
     * @param windowSizeLeft
     * @param windowSizeRight
     * @return 
     */
    public static double[] gaussian(double[] source, int windowSizeLeft, int windowSizeRight) {
        double[] result = new double[source.length - windowSizeLeft - windowSizeRight];

        int counter = 0;
        for (int i = 0; i < source.length; i++) {
            if (i > windowSizeLeft - 1 && i < source.length - windowSizeRight) {

                double sum = 0;
                double weightSum = 0;
                double weight = 0;
                //Get left side
                for (int x = i - windowSizeLeft; x < i; x++) {
                    weight = phi(source[x] - source[i]);
                    weightSum += weight;
                    sum += source[x] * weight;
                }
                //Get number itself
                weight = phi(source[i]);
                weightSum += weight;
                sum += source[i] * weight;
                //Get right side
                for (int y = i + windowSizeRight; y > i; y--) {
                    weight = phi(source[y] - source[i]);
                    weightSum += weight;
                    sum += source[y] * weight;
                }
                result[counter] = sum / weightSum;
                counter++;
            }
        }
        return result;
    }

    /**
     * Returns the probability at point x of the normal distribution.
     * @param x
     * @return 
     */
    public static double phi(double x) {
        return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
    }
}
